package com.xyy.saas.payment.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.xyy.ec.base.framework.rpc.ApiRPCResult;
import com.xyy.ec.pop.server.api.merchant.api.admin.CorporationAdminApi;
import com.xyy.ec.pop.server.api.merchant.dto.CorporationAndUserExtDto;
import com.xyy.ec.pop.server.api.merchant.dto.QueryCorporationParam;
import com.xyy.saas.payment.adapter.RouteManager;
import com.xyy.saas.payment.adapter.channel.fm.config.BankConfigManager;
import com.xyy.saas.payment.common.constants.ApolloConstants;
import com.xyy.saas.payment.common.constants.BaseConstants;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.common.constants.ChannelProductConstants;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.Pair;
import com.xyy.saas.payment.cores.vo.ContactInfo;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.enums.OpenAccountReportStatusEnum;
import com.xyy.saas.payment.enums.OpenAccountStatusEnum;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.manage.cores.vo.ResultVO;
import com.xyy.saas.payment.manage.cores.vo.UserSettleInfoVO;
import com.xyy.saas.payment.pojo.dto.*;
import com.xyy.saas.payment.pojo.vo.*;
import com.xyy.saas.payment.service.*;
import com.xyy.saas.payment.util.*;
import com.xyy.util.StringUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import util.AESUtil;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OpenAccountServiceImpl implements OpenAccountService {

    @Autowired
    private OpenpayInfoService openpayInfoService;
    @Autowired
    private PayAccountService payAccountService;
    @Autowired
    private RouteManager routeManager;
    @Autowired
    private ApplypayUserInfoService applypayUserInfoService;
    @Resource
    private ApplyOpenAccountStatusService applyOpenAccountStatusService;
    @Resource
    private LeafUtil leafUtil;
    @Resource
    private UserInfoService userInfoService;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;
    @Autowired
    private BankConfigManager bankConfigManager;
    @Autowired
    @Qualifier("ECPopGMVServiceImpl")
    private ECPopGMVService ecPopGMVService;
    @Reference(version = "1.0.0")
    private CorporationAdminApi corporationAdminApi;

    @Value("${wx.open_account.applymentUrl}")
    private String wxApplymentUrl;
    @Value("${wx.open_account.cancelUrl}")
    private String cancelUrl;
    @Value("${wx.file.uploadUrl}")
    private String wxFileUploadUrl;
    @Value("${wx.open_account.merchantId}")
    private String merchantId;
    @Value("${wx.open_account.certSerial}")
    private String certSerial;
    @Value("${wx.open_account.apiV3Key}")
    private String apiV3Key;
    @Value("${wx.open_account.merchantPrivateKey}")
    private String merchantPrivateKey;

    @Override
    public boolean openPay(String accountId, List<FBankPowerIdEnum> fBankPowerList) throws Exception {
        log.info("OpenAccountServiceImpl#openPay begin accountId:{}, fBankPowerList:{}", accountId, JSONUtils.toJSON(fBankPowerList));
        // 不做幂等，如果为开通可以直接开通，如果开通了，则在富民的协助下重新提交开通
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(accountId);
        ApplypayUserInfo applypayUserInfo = applypayUserInfoService.queryUserInfoByAccountId(accountId);
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(accountId);
        if(applypayUserInfo == null || null == thirdUserInfo) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        String companyShortName = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getBusinessLicenseInfo(),
                ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY))).getString("companyShortName");

        for(FBankPowerIdEnum powerIdEnum : fBankPowerList) {
            try {
                // 2.保存开通支付功能信息
                if(!updateOpenPayInfo(accountId, thirdUserInfo.getId(), powerIdEnum)) {
                    log.info("OpenAccountServiceImpl#openPay power is not fail, accountId:{}, powerId:{}", accountId, powerIdEnum.getPowerId());
                    // 失败跳过
                    continue;
                };

                // 3.开通支付功能
                OpenPayDto openPayDto = new OpenPayDto();
                openPayDto.setOpenPayChannel(powerIdEnum.getChannel());
                openPayDto.setBusinessId(payAccount.getBusinessUserId());
                openPayDto.setBusinessIdType(payAccount.getBusinessIdType());
                openPayDto.setCustMerchantNo(payAccount.getAccountId());
                openPayDto.setPowerId(powerIdEnum.getPowerId());
                openPayDto.setRate(powerIdEnum.getRate());
                openPayDto.setMchntType(powerIdEnum.getMchntType());
                openPayDto.setMchntShortName(companyShortName);
                openPayDto.setMcc(powerIdEnum.getMcc());
                Pair appIdInfo = FBankPowerIdEnum.getAppIdInfo(payAccount.getBusinessIdType(), powerIdEnum);
                openPayDto.setSubAppid(appIdInfo.getKey());
                openPayDto.setAppletName(appIdInfo.getValue());
                routeManager.openPay(ChannelConstants.CHANNEL_FUMIN_BANK_NAME, openPayDto);

                // 4.更新开通状态
                OpenpayInfo openpayInfo = new OpenpayInfo();
                openpayInfo.setAccountId(accountId);
                openpayInfo.setThirdUserInfoId(thirdUserInfo.getId());
                openpayInfo.setPowerId(powerIdEnum.getPowerId());
                openpayInfo.setStatus(OpenPayStatusEnum.OPENING.getStatus());
                openpayInfoService.updateStatusByPowerId(openpayInfo);
            }
            catch(Exception ex) {
                log.error(ex.getMessage() + " ,accountId:" + accountId +
                        ", thirdUserInfoId:" + thirdUserInfo.getId() + ", powerId:" + powerIdEnum.getPowerId() ,ex);
                // 更新支付能状态为失败
                OpenpayInfo openpayInfo = new OpenpayInfo();
                openpayInfo.setAccountId(accountId);
                openpayInfo.setThirdUserInfoId(thirdUserInfo.getId());
                openpayInfo.setPowerId(powerIdEnum.getPowerId());
                openpayInfo.setStatus(OpenPayStatusEnum.FAIL.getStatus());
                openpayInfoService.updateStatusByPowerId(openpayInfo);
            }
        }
        return true;
    }
    /**
     * 存在将其改为创建中，不存在则直接插入
     */
    private boolean updateOpenPayInfo(String accountId, Long thirdUserInfoId, FBankPowerIdEnum powerIdEnum) {
        List<OpenpayInfo> openpayInfos = openpayInfoService.queryUserInfoByAccountId(accountId, thirdUserInfoId);
        for (OpenpayInfo openpayInfo : openpayInfos) {
            if(powerIdEnum.getPowerId().equals(openpayInfo.getPowerId())) {
                // 只有失败的可以重试，前提：富民已经处理好，可以重试
                if(!OpenPayStatusEnum.FAIL.getStatus().equals(openpayInfo.getStatus())) {
                    return false;
                }
                OpenpayInfo record = new OpenpayInfo();
                record.setAccountId(accountId);
                record.setThirdUserInfoId(thirdUserInfoId);
                record.setPowerId(powerIdEnum.getPowerId());
                record.setStatus(OpenPayStatusEnum.CREATE.getStatus());
                openpayInfoService.updateStatusByPowerId(record);
                return true;
            }
        }
        // 没有在数据库中，直接新增
        OpenpayInfo openpayInfo = new OpenpayInfo();
        openpayInfo.setAccountId(accountId);
        openpayInfo.setThirdUserInfoId(thirdUserInfoId);
        openpayInfo.setPowerId(powerIdEnum.getPowerId());
        openpayInfo.setRate(powerIdEnum.getRate());
        openpayInfo.setStatus(OpenPayStatusEnum.CREATE.getStatus());
        openpayInfoService.insert(openpayInfo);
        return true;
    }

    @Override
    public ResultVO openAccountApplyment(String accountId, String thirdUserId) {
        log.info("AccountService#openAccountApplyment start apply. accountId:{}, thirdUserId:{}", accountId, thirdUserId);
        if(StringUtils.isEmpty(accountId) || StringUtils.isEmpty(thirdUserId)) {
            log.error("AccountService#openAccountApplyment param error. accountId:{}, thirdUserId:{}", accountId, thirdUserId);
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        // 判断
        ApplypayUserInfo applypayUserInfo = applypayUserInfoService.queryUserInfoByAccountId(accountId);
        if(null == applypayUserInfo) {
            log.error("AccountService#openAccountApplyment query ApplypayUserInfo is not exist. accountId:{}", accountId);
            return ResultVO.createError(ResultCodeEnum.USER_INFO_NOT_FOUND);
        }
        // 判断是否存在
        ApplyOpenAccountStatus queryParam = new ApplyOpenAccountStatus();
        queryParam.setAccountId(accountId);
        queryParam.setThirdUserId(thirdUserId);
        List<ApplyOpenAccountStatus> applyOpenAccountStatuseList = applyOpenAccountStatusService.selectByParam(queryParam);
        // 存在无需处理
        if(!applyOpenAccountStatuseList.isEmpty()) {
            log.info("AccountService#openAccountApplyment query ApplyOpenAccountStatus is exist. queryParam:{}", JSONUtils.toJSON(queryParam));
            return ResultVO.createSuccess();
        }

        String applyCode = leafUtil.getSnowflakeId();
        // 保存申请信息
        ApplyOpenAccountStatus entity = new ApplyOpenAccountStatus();
        entity.setAccountId(accountId);
        entity.setThirdUserId(thirdUserId);
        entity.setApplyCode(applyCode);
        entity.setStatus(OpenAccountApplyStatusEnum.IN_CREATE.getStatus());
        entity.setAuthorize(OpenAccountAuthorizeStatusEnum.AUTHORIZE_STATE_UNAUTHORIZED.getStatus());
        Date time = new Date();
        entity.setCreateTime(time);
        entity.setUpdateTime(time);
        int rows = applyOpenAccountStatusService.insert(entity);
        if(rows < 1) {
            log.error("AccountService#openAccountApplyment apply info insert error. accountId:{}, thirdUserId:{}", accountId, thirdUserId);
            return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
        }

        try {
            UserInfo userInfo = userInfoService.queryUserInfoByAccountId(accountId);
            if(null == userInfo) {
                log.error("AccountService#openAccountApplyment userInfo is exist. accountId:{}, thirdUserId:{}", accountId, thirdUserId);
                return ResultVO.createError(ResultCodeEnum.USER_INFO_NOT_FOUND);
            }
            String userInfoJson = AESUtil.decrypt(userInfo.getContactInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY));
            String basicInfoJson = AESUtil.decrypt(userInfo.getBasicInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY));
            String picInfoJson = AESUtil.decrypt(userInfo.getPicInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY));
            ContactInfo contactInfo = JSONUtils.parse2List(userInfoJson,ContactInfo.class).get(0);
            Map<String, String> picInfoMap = JSONUtils.parse2Map(picInfoJson, String.class, String.class);
            UserDto basicUserDto = JSONUtils.parse2Object(basicInfoJson, UserDto.class);
            IdentityInfo identityInfo = basicUserDto.getIdentityInfo();

            // 构造微信平台证书
            WxUtil.WxCertEntity wxCert = WxUtil.buildWxCert(merchantId, apiV3Key, certSerial, merchantPrivateKey);
            //  =========  封装参数 start  ============

            Map<String, Object> applyParam = new HashMap<>();
            applyParam.put("channel_id", merchantId);
            applyParam.put("business_code", applyCode);
            // 联系人信息
            Map<String, String> contactInfoParam = new HashMap<>();
            contactInfoParam.put("contact_type", "LEGAL");
            contactInfoParam.put("name", wxCert.encryptOAEP(contactInfo.getContactName()));
            contactInfoParam.put("mobile", wxCert.encryptOAEP(contactInfo.getContactCertMobile()));
            contactInfoParam.put("id_card_number", wxCert.encryptOAEP(contactInfo.getContactCertNo()));
            applyParam.put("contact_info", contactInfoParam);

            // 主体信息
            Map<String, Object> subjectInfoParam = new HashMap<>();
            // 企业
            subjectInfoParam.put("subject_type", "SUBJECT_TYPE_ENTERPRISE");
            Map<String, String> businessLicenceInfoParam = new HashMap<>();
            businessLicenceInfoParam.put("licence_number", identityInfo.getId());
            businessLicenceInfoParam.put("licence_copy", wxCert.uploadImg2Wx(picInfoMap.get(String.valueOf(PictureEnum.BUSINESS_CERT_PIC.getCode())), wxFileUploadUrl));
            businessLicenceInfoParam.put("merchant_name", basicUserDto.getName());
            businessLicenceInfoParam.put("legal_person", contactInfo.getContactName());
            businessLicenceInfoParam.put("company_address", basicUserDto.getAddress());
            String validFrom = identityInfo.getValidFrom();
            String validUntil = ("2099-12-31".equals(identityInfo.getValidUntil()) || "2999-12-31".equals(identityInfo.getValidUntil()))
                    ? "forever": identityInfo.getValidUntil();
            businessLicenceInfoParam.put("licence_valid_date", JSONUtils.toJSON(Arrays.asList(validFrom, validUntil)));
            subjectInfoParam.put("business_licence_info", businessLicenceInfoParam);
            applyParam.put("subject_info", subjectInfoParam);

            // 法人信息
            Map<String, Object> identificationInfoParam = new HashMap<>();
            // 证件类型-身份证
            identificationInfoParam.put("identification_type", "IDENTIFICATION_TYPE_IDCARD");
            identificationInfoParam.put("identification_name", wxCert.encryptOAEP(StringUtils.trimToEmpty(contactInfo.getContactName())));
            identificationInfoParam.put("identification_number", wxCert.encryptOAEP(StringUtils.upperCase(contactInfo.getContactCertNo())));
            String contactCertValidFrom = contactInfo.getContactCertValidFrom();
            // 永远为 forever 2099为了兼容之前传参错误导致的数据问题
            String contactCertValidUntil = ("2099-12-31".equals(contactInfo.getContactCertValidUntil())
                    || "2999-12-31".equals(contactInfo.getContactCertValidUntil()))?
                    "forever":
                    contactInfo.getContactCertValidUntil();

            identificationInfoParam.put("identification_valid_date", JSONUtils.toJSON(Arrays.asList(contactCertValidFrom, contactCertValidUntil)));
            identificationInfoParam.put("identification_front_copy", wxCert.uploadImg2Wx(picInfoMap.get(String.valueOf(PictureEnum.LEGAL_CERT_FRONT.getCode())), wxFileUploadUrl));
            identificationInfoParam.put("identification_back_copy", wxCert.uploadImg2Wx(picInfoMap.get(String.valueOf(PictureEnum.LEGAL_CERT_BACK.getCode())), wxFileUploadUrl));
            String issuingAddress = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getLegalInfo(),
                    ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY))).getString("issuingAddress");
            identificationInfoParam.put("identification_address", wxCert.encryptOAEP(StringUtils.trimToEmpty(issuingAddress)));
            identificationInfoParam.put("owner", true);
            applyParam.put("identification_info", identificationInfoParam);
            // 补充材料
            if(!StringUtils.isEmpty(applypayUserInfo.getSubMchid())) {
                Map<String, Object> additionInfo = new HashMap<>();
                additionInfo.put("confirm_mchid_list", Arrays.asList(applypayUserInfo.getSubMchid()));
                applyParam.put("addition_info", additionInfo);
            }
            //   =========  封装参数 end  ============

            //   =========  发送  ============
            log.info("AccountService#openAccountApplyment send to wx. url:{}, param:{}", wxApplymentUrl, JSONUtils.toJSON(applyParam));
            String response = "";
            Map<String, String> header = new HashMap<>();
            header.put("Accept", "application/json");
            header.put("Content-type", "application/json; charset=utf-8");
            header.put("Wechatpay-Serial", wxCert.getCertSerialNo());
            CloseableHttpResponse closeableHttpResponse = wxCert.wxHttpPostRequestRetResp(wxApplymentUrl, JSONUtils.toJSON(applyParam), header);
            response = EntityUtils.toString(closeableHttpResponse.getEntity());
            // 判断是否申请成功
            if(200 != closeableHttpResponse.getStatusLine().getStatusCode()) {
                log.error("AccountService#openAccountApplyment Applyment fail. response:{}", response);
                // 申请失败 - 申请单置为无效
                ApplyOpenAccountStatus record = applyOpenAccountStatusService.selectByApplyCode(applyCode);
                ApplyOpenAccountStatus updObj = new ApplyOpenAccountStatus();
                updObj.setId(record.getId());
                updObj.setStatus(OpenAccountApplyStatusEnum.APPLYMENT_STATE_CANCELED.getStatus());
                updObj.setRejectReason(response);
                updObj.setUpdateTime(new Date());
                int lines = applyOpenAccountStatusService.updateByPrimaryKeySelective(updObj);
                if(lines < 1) {
                    log.error("AccountService#openAccountApplyment execute update sql error. updObj:{}",JSONUtils.toJSON(updObj));
                    return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
                }
                return ResultVO.createError(ResultCodeEnum.WX_APPLYMENT_FAIL);
            }
            log.info("AccountService#openAccountApplyment response from wx. response:{}", response);
            String applymentId = JSONUtils.parse2Map(response, String.class, String.class).get("applyment_id");

            // 更新库
            ApplyOpenAccountStatus record = applyOpenAccountStatusService.selectByApplyCode(applyCode);
            ApplyOpenAccountStatus updObj = new ApplyOpenAccountStatus();
            updObj.setId(record.getId());
            updObj.setApplymentId(applymentId);
            updObj.setStatus(OpenAccountApplyStatusEnum.APPLYMENT_STATE_WAITTING_FOR_AUDIT.getStatus());
            updObj.setUpdateTime(new Date());
            int lines = applyOpenAccountStatusService.updateByPrimaryKeySelective(updObj);
            if(lines < 1) {
                log.error("AccountService#openAccountApplyment execute update sql error. updObj:{}",JSONUtils.toJSON(updObj));
                return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
            }
        } catch (Exception e) {
            log.error("AccountService#openAccountApplyment exception.", e);
            return ResultVO.createError(e.getMessage());
        }
        log.info("AccountService#openAccountApplyment end apply.");
        return ResultVO.createSuccess();
    }

    @Override
    public ResultVO againOpenAccountApplyment(String applyCode) {
        ApplyOpenAccountStatus applyOpenAccountStatus = applyOpenAccountStatusService.selectByApplyCode(applyCode);
        if(null == applyOpenAccountStatus) {
            return ResultVO.create(ResultCodeEnum.WX_APPLYMENT_NOT_FOUND);
        }
        // 创建中，已通过，已冻结的无需处理
        if(OpenAccountApplyStatusEnum.APPLYMENT_STATE_PASSED.getStatus() == applyOpenAccountStatus.getStatus()
                || OpenAccountApplyStatusEnum.APPLYMENT_STATE_FREEZED.getStatus() == applyOpenAccountStatus.getStatus()
                || OpenAccountApplyStatusEnum.IN_CREATE.getStatus() == applyOpenAccountStatus.getStatus()) {
            return ResultVO.create(ResultCodeEnum.WX_APPLYMENT_CANNOT_AGAIN_APPLY);
        }
        // 撤销申请 -- 已作废的可以直接跳过
        if(OpenAccountApplyStatusEnum.APPLYMENT_STATE_CANCELED.getStatus() != applyOpenAccountStatus.getStatus()) {
            try {
                // 构造微信平台证书
                WxUtil.WxCertEntity wxCert = WxUtil.buildWxCert(merchantId, apiV3Key, certSerial, merchantPrivateKey);
                String url = cancelUrl.replace("{applyment_id}", applyOpenAccountStatus.getApplymentId());
                Map<String, String> header = new HashMap<>();
                header.put("Accept","application/json");
                header.put("Content-type","application/json; charset=utf-8");
                CloseableHttpResponse response = wxCert.wxHttpPostRequestRetResp(url, new HashMap<>(), header);
                if (response.getStatusLine().getStatusCode() != 204) {
                    return ResultVO.create(ResultCodeEnum.WX_APPLYMENT_CANCEL_FAIL);
                }
            } catch (Exception e) {
                log.error("OpenAccountServiceImpl#againOpenAccountApplyment cancel applyment fail. applyCode:{}", applyCode, e);
                return ResultVO.create(ResultCodeEnum.WX_APPLYMENT_CANCEL_FAIL);
            }
        }
        // 先删除数据在进行后面操作
        int rows = applyOpenAccountStatusService.removeById(applyOpenAccountStatus.getId());
        if(rows < 1) {
            log.error("OpenAccountServiceImpl#againOpenAccountApplyment date remove fail. applyCode:{}", applyCode);
            return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
        }
        // 重新申请
        log.info("OpenAccountServiceImpl#againOpenAccountApplyment start openAccountApplyment applyOpenAccountStatus:{}", JSONUtils.toJSON(applyOpenAccountStatus));
        return openAccountApplyment(applyOpenAccountStatus.getAccountId(), applyOpenAccountStatus.getThirdUserId());
    }

    @Override
    public ResultVO<OpenAccountStatusSummaryVO> excelOpenAccount() {
        log.info("OpenAccountServiceImpl#excelOpenAccount start.");

        List<OpenAccountStatusVO> resultList = new ArrayList<>();
        int pageNum = 1;
        int pageSize = BaseConstants.DEFAULT_PAGE_SIZE;
        QueryCorporationParam param = new QueryCorporationParam();
        param.setPageSize(pageSize);
        param.setSettlementType((byte) 0);
        param.setState((byte) 5);
        while (true) {
            param.setPageNum(pageNum++);
            ApiRPCResult<PageInfo<CorporationAndUserExtDto>> pageInfoApiRPCResult = corporationAdminApi.listCorporations(param);
            PageInfo<CorporationAndUserExtDto> data = pageInfoApiRPCResult.getData();
            List<CorporationAndUserExtDto> list = data.getList();
            log.info("OpenAccountServiceImpl#excelOpenAccount param={}, pageList size={}", JSON.toJSONString(param), list.size());

            for (CorporationAndUserExtDto corporationAndUserExtDto : list) {
                OpenAccountStatusVO item = new OpenAccountStatusVO();

                String prov = corporationAndUserExtDto.getProv();
                String companyName = corporationAndUserExtDto.getCompanyName();
                String orgId = corporationAndUserExtDto.getOrgId();
                item.setBusinessId(orgId);
                item.setName(companyName);
                item.setPrvo(prov);

                // 去掉测试
                if(companyName.contains("测试")){
                    continue;
                }
                // 计算GMV
                ECPopGMV ecPopGMV = ecPopGMVService.queryByBusinessId(orgId);
                if(null != ecPopGMV) {
                    item.setGmv(ecPopGMV.getGmv());
                    // 入驻时间
                    item.setOpenAccountTime(ecPopGMV.getBusinessCreateTime());
                }else{
                    item.setGmv(0.0);
                }

                // 算总数
                item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION.getCode());
                resultList.add(item);

                // 有accoutnId 说明提交过材料
                String accountId = payAccountService.getAccountIdByBusiness("ec_pop", orgId);
                if(StringUtils.isEmpty(accountId)) {
                    item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_NO_TDATA.getCode());
                    continue;
                }
                PayAccount payAccount = payAccountService.getPayAccountByAccountId(accountId);
                item.setLastUpdateTime(payAccount.getCtime());

                ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(accountId);
                if(thirdUserInfo !=null && StringUtils.isNotBlank(thirdUserInfo.getThirdUserId()) && !thirdUserInfo.getUserId().equals(thirdUserInfo.getThirdUserId())) {
                    if (thirdUserInfo.getThirdType().equals("qifutong")) {
                        item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_FM_BASE_ACCOUNT_SUCCESS.getCode());
                        item.setLastUpdateTime(thirdUserInfo.getUtime());
                    } else if (thirdUserInfo.getThirdType().equals("pingan")) {
                        item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_PINGAN_BASE_ACCOUNT_SUCCESS.getCode());
                        item.setLastUpdateTime(thirdUserInfo.getUtime());
                    } else {
                        item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_NO_TDATA.getCode());
                        item.setLastUpdateTime(thirdUserInfo.getUtime());
                    }
                } else {
                    item.setStatus(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_NO_TDATA.getCode());
                    if (thirdUserInfo != null) {
                        item.setLastUpdateTime(thirdUserInfo.getUtime());
                    }
                }
            }

            if(!data.isHasNextPage()) {
                break;
            }
        }
        resultList = resultList.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(OpenAccountStatusVO::getBusinessId))), ArrayList::new)
        );
        OpenAccountStatusSummaryVO resultVo = new OpenAccountStatusSummaryVO();
        resultList.stream().forEach(item -> {
            OpenAccountReportStatusEnum e = OpenAccountReportStatusEnum.getEnum(item.getStatus());
            item.setStatusDesc(e.getDesc());

            // 计算总数
            resultVo.setSumCorporation(resultVo.getSumCorporation()+1);
            // 未提交资料
            if(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_NO_TDATA.getCode().equals(item.getStatus())) {
                resultVo.setSumCorporationForNotData(resultVo.getSumCorporationForNotData()+1);
            }
            // 开富民基本户成功商户
            if(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_FM_BASE_ACCOUNT_SUCCESS.getCode().equals(item.getStatus())) {
                resultVo.setSumCorporationForFmBaseAccountSuccess(resultVo.getSumCorporationForFmBaseAccountSuccess()+1);
            }
            // 换签平安成功商户
            if(OpenAccountReportStatusEnum.SUM_CORPORATION_FOR_PINGAN_BASE_ACCOUNT_SUCCESS.getCode().equals(item.getStatus())) {
                resultVo.setSumCorporationForPinganBaseAccountSuccess(resultVo.getSumCorporationForPinganBaseAccountSuccess()+1);
            }

        });
        resultVo.setOpenAccountStatusList(resultList);

        log.info("商户总数:{},未提交开户资料商户:{},开富民基本户成功商户:{},换签平安成功商户:{},datalistsize:{}",
                resultVo.getSumCorporation(),
                resultVo.getSumCorporationForNotData(),
                resultVo.getSumCorporationForFmBaseAccountSuccess(),
                resultVo.getSumCorporationForPinganBaseAccountSuccess(),
                resultVo.getOpenAccountStatusList().size());

        return ResultVO.createSuccess(resultVo);
    }

    @Override
    public OpenAccountStepStatusVO queryOpenAccountStep(OpenAccountStatusQueryParam param) throws PaymentException {
        log.info("OpenAccountServiceImpl#queryOpenAccountStep start param:{}",JSONUtils.toJSON(param));
        checkOpenAccountStatusQueryParam(param);
        // 初始化数据结构
        OpenAccountStepStatusVO result = new OpenAccountStepStatusVO();
        List<Map<String, Object>> step = new ArrayList<>();
        result.setStep(step);

        // 查询
        String accountId = payAccountService.getAccountIdByBusiness(param.getBusinessIdType(), param.getBusinessId());
        if(StringUtils.isEmpty(accountId)) {
            // 0 未提交资料
            addOpenAccountStep(step, OpenAccountStatusEnum.UN_COMMIT, null, null);
            return result;
        }
        // 1 提交资料了
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(accountId);
        addOpenAccountStep(step, OpenAccountStatusEnum.COMMIT_DATA_2_FM, payAccount.getCtime(), "平台用户ID:"+accountId);

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(accountId);
        if(thirdUserInfo==null || thirdUserInfo.getUserId().equals(thirdUserInfo.getThirdUserId())) {
            // 102 开基本户失败
            addOpenAccountStep(step, OpenAccountStatusEnum.OPEN_BASE_ACCOUNT_FAIL, thirdUserInfo==null?null:thirdUserInfo.getUtime(), null);
            return result;
        }
        // 2 开基本户成功
        addOpenAccountStep(step, OpenAccountStatusEnum.OPEN_BASE_ACCOUNT_SUCCESS, thirdUserInfo.getUtime(), "富民虚户号:"+thirdUserInfo.getThirdUserId());

        ApplypayUserInfo applypayUserInfo = applypayUserInfoService.queryUserInfoByAccountId(accountId);
        if(null == applypayUserInfo) {
            return result;
        }
        // 3 已向富民提交了进件资料
        addOpenAccountStep(step, OpenAccountStatusEnum.COMMIT_CREATE_PAY_2_FM, thirdUserInfo.getUtime(), null);

        // 3 富民进件中待审核
        if(OpenPayStatusEnum.CREATE.getStatus().equals(applypayUserInfo.getStatus())
                || OpenPayStatusEnum.OPENING.getStatus().equals(applypayUserInfo.getStatus())) {
            // 如果超过一天 - 待签署邮件（时间可能不准）
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH)-1);
            Date yesterdayFirstTime = null;
            try {
                yesterdayFirstTime = DateUtil.getDayFirstTime(calendar.getTime());
            } catch (Exception e) {
                log.error("",e);
                throw new PaymentException("时间解析失败!");
            }
            if(yesterdayFirstTime.before(thirdUserInfo.getUtime())) {
                // 4 审核中
                addOpenAccountStep(step, OpenAccountStatusEnum.CREATE_PAY_WAIT_AUDIT, applypayUserInfo.getUpdateTime(), null);
                return result;
            } else {
                // 5 待用户签署协议
                addOpenAccountStep(step, OpenAccountStatusEnum.WAIT_SIGN, applypayUserInfo.getUpdateTime(), null);
                return result;
            }
        }
        // 104 审核失败
        if(OpenPayStatusEnum.FAIL.getStatus().equals(applypayUserInfo.getStatus())) {
            addOpenAccountStep(step, OpenAccountStatusEnum.CREATE_PAY_FAIL, applypayUserInfo.getUpdateTime(), applypayUserInfo.getMessage());
            return result;
        }
        // 6 进件成功 - 特约商户号
        addOpenAccountStep(step, OpenAccountStatusEnum.CREATE_PAY_PASS, applypayUserInfo.getUpdateTime(), "特约商户号："+applypayUserInfo.getSubMchid());

        // 7 开支付功能情况
        List<OpenpayInfo> openpayInfos = openpayInfoService.queryUserInfoByAccountId(accountId, thirdUserInfo.getId());
        StringBuilder sb = new StringBuilder();
        Date openPayTime = null;
        for (OpenpayInfo openpayInfo : openpayInfos) {
            // 历史数据兼容，跳过不支持的支付功能
            if("32".equals(openpayInfo.getPowerId())) {
                continue;
            }

            openPayTime = openpayInfo.getUpdateTime();
            FBankPowerIdEnum powerIdEnum = FBankPowerIdEnum.getPowerIdById(openpayInfo.getPowerId());
            // 获取开通支付功能状态
            OpenPayStatusEnum openPayStatus = null;
            for (OpenPayStatusEnum item : OpenPayStatusEnum.values()){
                if(item.getStatus().equals(openpayInfo.getStatus())) {
                    openPayStatus = item;
                }
            }
            sb.append(powerIdEnum).append("(").append(powerIdEnum.getPowerId()).append("):").append(openPayStatus == null ? "" : openPayStatus.getDesc()).append(";");
        }
        addOpenAccountStep(step, OpenAccountStatusEnum.OPEN_PAY_STATUS, openPayTime, sb.toString());

        //
        ApplyOpenAccountStatus queryParam = new ApplyOpenAccountStatus();
        queryParam.setAccountId(accountId);
        queryParam.setThirdUserId(thirdUserInfo.getThirdUserId());
        List<ApplyOpenAccountStatus> applyOpenAccountStatuses = applyOpenAccountStatusService.selectByParam(queryParam);
        if(applyOpenAccountStatuses.size() != 1) {
            throw new PaymentException("开户意愿申请单异常!");
        }
        ApplyOpenAccountStatus applyOpenAccountStatus = applyOpenAccountStatuses.get(0);
        // 8 已提交开户意愿申请单
        addOpenAccountStep(step, OpenAccountStatusEnum.COMMIT_DATA_2_WX, applyOpenAccountStatus.getCreateTime(), "申请单号：支付平台("+applyOpenAccountStatus.getApplyCode()+") 微信平台("+applyOpenAccountStatus.getApplymentId()+")");

        if(applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_REJECTED.getStatus()
                || applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_CANCELED.getStatus()) {
            // 开户意愿申请失败
            addOpenAccountStep(step, OpenAccountStatusEnum.APPLYMENT_FAIL, applyOpenAccountStatus.getUpdateTime(), applyOpenAccountStatus.getRejectReason());
            return result;
        }

        if(applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_WAITTING_FOR_CONFIRM_CONTACT.getStatus()
                || applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_WAITTING_FOR_CONFIRM_LEGALPERSON.getStatus()) {
            // 待法人扫码
            addOpenAccountStep(step, OpenAccountStatusEnum.WAIT_CONFIRM, applyOpenAccountStatus.getUpdateTime(), null);
            return result;
        }

        if(applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_PASSED.getStatus()
                || applyOpenAccountStatus.getStatus() == OpenAccountApplyStatusEnum.APPLYMENT_STATE_FREEZED.getStatus()) {
            // 审核通过
            addOpenAccountStep(step, OpenAccountStatusEnum.APPLYMENT_PASS, applyOpenAccountStatus.getUpdateTime(), null);

            // 授权代表可能开户成功
            if(applyOpenAccountStatus.getAuthorize() == OpenAccountAuthorizeStatusEnum.AUTHORIZE_STATE_AUTHORIZED.getStatus()) {
                addOpenAccountStep(step, OpenAccountStatusEnum.OPEN_ACCOUNT_SUCCESS, applyOpenAccountStatus.getUpdateTime(), null);
                return result;
            }else{
                addOpenAccountStep(step, OpenAccountStatusEnum.WAIT_AUTH, applyOpenAccountStatus.getUpdateTime(), null);
                return result;
            }
        }
        return result;
    }

    private void checkOpenAccountStatusQueryParam(OpenAccountStatusQueryParam param) throws PaymentException {
        if(null == param || StringUtils.isEmpty(param.getBusinessId()) || StringUtils.isEmpty(param.getBusinessIdType())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
    }

    private void addOpenAccountStep(List<Map<String, Object>> steps, OpenAccountStatusEnum step, Date time, String msg){
        Map<String, Object> map = new HashMap<>();
        map.put("step", step.getStep());
        map.put("desc", step.getDesc());
        map.put("time", null==time ? "":DateUtil.format(time, DateUtil.DATEFORMATSECOND));
        map.put("msg", null==msg ? "":msg);
        steps.add(map);
    }

    @Override
    public OpenAccountInfoVO queryOpenAccountInfo(OpenAccountInfoQueryParam param) throws PaymentException {
        ResultVO resultVO = checkOpenAccountInfoParam(param);
        if(!resultVO.isSuccess()) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, resultVO.getMsg());
        }

        OpenAccountInfoVO result = new OpenAccountInfoVO();

        // ================ user info ============
        // 用户信息
        UserInfo userInfo = userInfoService.queryUserInfoByAccountId(param.getAccountId());
        PayAccount payAccount = payAccountService.getPayAccountByAccountId(param.getAccountId());
        if(null == userInfo) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }
        UserInfoVO userInfoVO = new UserInfoVO();
        String telNo = null;
        Map<String, String> picMap = null;
        List<ContactInfo> contactInfos = null;
        UserDto basicInfo = null;
        SettleAccountDto settleInfo = null;
        try {
            String userInfoKey = ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY);
            telNo = AESUtil.decrypt(userInfo.getTelNo(), userInfoKey);
            String contactInfoStr = AESUtil.decrypt(userInfo.getContactInfo(), userInfoKey);
            picMap = JSONUtils.parse2Map(AESUtil.decrypt(userInfo.getPicInfo(), userInfoKey), String.class, String.class);
            contactInfos = JSONUtils.parse2List(contactInfoStr, ContactInfo.class);
            String basicInfoStr = AESUtil.decrypt(userInfo.getBasicInfo(), userInfoKey);
            basicInfo = JSONUtils.parse2Object(basicInfoStr, UserDto.class);
            String settleInfoStr = AESUtil.decrypt(userInfo.getSettleInfo(), userInfoKey);
            settleInfo = JSONUtils.parse2Object(settleInfoStr, SettleAccountDto.class);
        } catch (Exception e) {
            log.error("OpenAccountServiceImpl#queryOpenAccountInfo decrypt error.", e);
            throw new PaymentException(ResultCodeEnum.DECRYPT_ERROR);
        }
        //
        userInfoVO.setAccount(settleInfo.getRecipient().getAccount());
        userInfoVO.setSettleName(settleInfo.getRecipient().getName());
        userInfoVO.setSettleMobile(settleInfo.getRecipient().getMobile());
        userInfoVO.setOpenBank(settleInfo.getRecipient().getOpenBank());
        userInfoVO.setSubBank(settleInfo.getRecipient().getSubBank());
        userInfoVO.setSubBankCode(settleInfo.getRecipient().getSubBankCode());
        userInfoVO.setCardType(String.valueOf(settleInfo.getRecipient().getCardType()));
        userInfoVO.setCallbackAddress(settleInfo.getCallbackAddress());
        userInfoVO.setBusinessIdType(settleInfo.getBusinessIdType());
        userInfoVO.setBusinessUserName(settleInfo.getBusinessUserName());
        userInfoVO.setAccountType(settleInfo.getAccountType());

        userInfoVO.setCardId(basicInfo.getIdentityInfo().getId());
        userInfoVO.setIdentityType(basicInfo.getIdentityInfo().getType());
        userInfoVO.setProfession(basicInfo.getIdentityInfo().getProfession());
        userInfoVO.setValidFrom(basicInfo.getIdentityInfo().getValidFrom());
        userInfoVO.setValidUntil(basicInfo.getIdentityInfo().getValidUntil());
        userInfoVO.setIndustry(basicInfo.getIdentityInfo().getIndustry());

        userInfoVO.setName(basicInfo.getName());
        userInfoVO.setAddress(basicInfo.getAddress());

        userInfoVO.setAccountId(param.getAccountId());

        if(contactInfos != null) {
            for (ContactInfo contactInfo : contactInfos) {
                contactInfo.setUserId(payAccount.getBusinessUserId());
            }
        }

        userInfoVO.setContactInfos(contactInfos);

        userInfoVO.setMobile(telNo);

        userInfoVO.setPicMap(picMap);

        result.setUserInfo(userInfoVO);

        // ================ third user info ============
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(param.getAccountId());
        if(null != thirdUserInfo) {
            ThirdUserInfoVO thirdUserInfoVO = new ThirdUserInfoVO();
            BeanUtils.copyProperties(thirdUserInfo, thirdUserInfoVO);
            thirdUserInfoVO.setCtime(DateUtil.format(thirdUserInfo.getCtime(), DateUtil.DATEFORMATSECOND));
            thirdUserInfoVO.setUtime(DateUtil.format(thirdUserInfo.getUtime(), DateUtil.DATEFORMATSECOND));
            result.setThirdUserInfo(thirdUserInfoVO);
        }

        // ================ create pay info ============
        // 报件信息
        ApplypayUserInfo applypayUserInfo = applypayUserInfoService.queryUserInfoByAccountId(param.getAccountId());
        if(null != applypayUserInfo) {
            JSONObject businessLicenseInfo = null;
            try {
                businessLicenseInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getBusinessLicenseInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            } catch (Exception e) {
                log.error("OpenAccountServiceImpl#queryOpenAccountInfo decrypt error.", e);
                throw new PaymentException(ResultCodeEnum.DECRYPT_ERROR);
            }
            JSONObject createPaySettleInfo = null;
            JSONObject createPayContactInfo = null;
            JSONObject createPayAccountInfo = null;
            JSONObject createPayLegalInfo = null;
            JSONObject createPayShareholderInfo = null;
            JSONObject createPayBeneficiaryInfo = null;
            JSONObject createPayImgsInfo = null;
            try {
                createPaySettleInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getSettleInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayContactInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getContactInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayAccountInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getAccountInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayLegalInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getLegalInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayShareholderInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getShareholderInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayBeneficiaryInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getBeneficiaryInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
                createPayImgsInfo = JSONObject.parseObject(AESUtil.decrypt(applypayUserInfo.getImgsInfo(), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            } catch (Exception e) {
                log.error("OpenAccountServiceImpl#queryOpenAccountInfo decrypt error.", e);
                throw new PaymentException(ResultCodeEnum.DECRYPT_ERROR);
            }
            CreateAllPayVO createAllPayVO = new CreateAllPayVO();
            createAllPayVO.setUserId(applypayUserInfo.getAccountId());
            createAllPayVO.setBusinessId(payAccount.getBusinessUserId());
            createAllPayVO.setBusinessIdType(payAccount.getBusinessIdType());
            String fuminBankMch = bankConfigManager.getFuminBankMch(payAccount.getBusinessIdType());
            createAllPayVO.setMerchantNo(fuminBankMch);
            createAllPayVO.setAccountChannel("aggregate");
            createAllPayVO.setCustMerchantNo(payAccount.getAccountId());
            // 营业执照信息
            createAllPayVO.setCompanyType(businessLicenseInfo.getString("companyType"));
            createAllPayVO.setCompanyShortName(businessLicenseInfo.getString("companyShortName"));
            createAllPayVO.setBusinessScope(businessLicenseInfo.getString("businessScope"));
            createAllPayVO.setRegisteredCapital(businessLicenseInfo.getString("registeredCapital"));
            createAllPayVO.setRegisterAddress(businessLicenseInfo.getString("registerAddress"));
            createAllPayVO.setProvinces(businessLicenseInfo.getString("provinces"));
            createAllPayVO.setCity(businessLicenseInfo.getString("city"));
            createAllPayVO.setDistrict(businessLicenseInfo.getString("district"));
            createAllPayVO.setCustomerPhone(businessLicenseInfo.getString("customerPhone"));
            createAllPayVO.setLicenseTermStart(businessLicenseInfo.getString("licenseTermStart"));
            createAllPayVO.setLicenseTermEnd(businessLicenseInfo.getString("licenseTermEnd"));

            // 结算信息
            createAllPayVO.setAccountName(createPaySettleInfo.getString("accountName"));
            createAllPayVO.setAcct(createPaySettleInfo.getString("acct"));
            createAllPayVO.setBankCd(createPaySettleInfo.getString("bankCd"));
            createAllPayVO.setBranchBankCd(createPaySettleInfo.getString("branchBankCd"));
            createAllPayVO.setBranchBankName(createPaySettleInfo.getString("branchBankName"));
            createAllPayVO.setMobileNo(createPaySettleInfo.getString("mobileNo"));

            // 联系人信息
            createAllPayVO.setContactName(createPayContactInfo.getString("contactName"));
            createAllPayVO.setContactPhone(createPayContactInfo.getString("contactPhone"));
            createAllPayVO.setContactEmail(createPayContactInfo.getString("contactEmail"));
            createAllPayVO.setContactAddress(createPayContactInfo.getString("contactAddress"));
            createAllPayVO.setContactProvCd(createPayContactInfo.getString("contactProvCd"));
            createAllPayVO.setContactCityCd(createPayContactInfo.getString("contactCityCd"));
            createAllPayVO.setContactContryCd(createPayContactInfo.getString("contactContryCd"));

            // 商家账号信息
            createAllPayVO.setMobileNo(createPayAccountInfo.getString("mobile"));
            createAllPayVO.setEmail(createPayAccountInfo.getString("email"));

            // 法人信息
            createAllPayVO.setContactPhone(createPayLegalInfo.getString("corporatePhone"));
            createAllPayVO.setIdCardLegalTermStart(createPayLegalInfo.getString("idCardLegalTermStart"));
            createAllPayVO.setIdCardLegalTermEnd(createPayLegalInfo.getString("idCardLegalTermEnd"));
            createAllPayVO.setCitizenship(createPayLegalInfo.getString("citizenship"));
            createAllPayVO.setSex(createPayLegalInfo.getString("sex"));
            createAllPayVO.setDomicileAddress(createPayLegalInfo.getString("domicileAddress"));
            createAllPayVO.setIssuingAddress(createPayLegalInfo.getString("issuingAddress"));

            // 股东信息
            createAllPayVO.setControllerName(createPayShareholderInfo.getString("controllerName"));
            createAllPayVO.setControllerType(createPayShareholderInfo.getString("controllerType"));
            createAllPayVO.setControllerNumber(createPayShareholderInfo.getString("controllerNumber"));
            createAllPayVO.setControllerStart(createPayShareholderInfo.getString("controllerStart"));
            createAllPayVO.setControllerEnd(createPayShareholderInfo.getString("controllerEnd"));

            // 受益人信息
            createAllPayVO.setFavoreeName(createPayBeneficiaryInfo.getString("favoreeName"));
            createAllPayVO.setFavoreeType(createPayBeneficiaryInfo.getString("favoreeType"));
            createAllPayVO.setFavoreeNumber(createPayBeneficiaryInfo.getString("favoreeNumber"));
            createAllPayVO.setFavoreeStart(createPayBeneficiaryInfo.getString("favoreeStart"));
            createAllPayVO.setFavoreeEnd(createPayBeneficiaryInfo.getString("favoreeEnd"));
            createAllPayVO.setFavoreeAddress(createPayBeneficiaryInfo.getString("favoreeAddress"));

            // 图片信息
            createAllPayVO.setShopEntrancePic(createPayImgsInfo.getString("shopEntrancePic"));
            createAllPayVO.setBusinessLicensePic(createPayImgsInfo.getString("businessLicensePic"));
            createAllPayVO.setOpenAccountPic(createPayImgsInfo.getString("openAccountPic"));
            createAllPayVO.setIdCardLegalZPic(createPayImgsInfo.getString("idCardLegalZPic"));
            createAllPayVO.setIdCardLegalFPic(createPayImgsInfo.getString("idCardLegalFPic"));

            createAllPayVO.setCallback(applypayUserInfo.getCallback());
            // 报件状态
            for (OpenPayStatusEnum openPayStatus : OpenPayStatusEnum.values()) {
                if (openPayStatus.getStatus().equals(applypayUserInfo.getStatus())) {
                    createAllPayVO.setStatus(openPayStatus.getDesc());
                    createAllPayVO.setMessage(applypayUserInfo.getMessage());
                }
            }

            // 支付功能列表
            List<OpenpayInfo> openpayInfos = openpayInfoService.queryUserInfoByAccountId(param.getAccountId(), thirdUserInfo.getId());
            List<OpenPayVO> openPayList = new ArrayList<>();
            createAllPayVO.setOpenPayList(openPayList);
            for (OpenpayInfo openpayInfo : openpayInfos) {
                OpenPayVO item = new OpenPayVO();
                BeanUtils.copyProperties(openpayInfo, item, "status");
                for (OpenPayStatusEnum openPayStatus : OpenPayStatusEnum.values()) {
                    if (openPayStatus.getStatus().equals(openpayInfo.getStatus())) {
                        item.setStatus(openPayStatus.getDesc());
                    }
                }
                item.setCreateTime(DateUtil.format(openpayInfo.getCreateTime(), DateUtil.DATEFORMATSECOND));
                item.setUpdateTime(DateUtil.format(openpayInfo.getUpdateTime(), DateUtil.DATEFORMATSECOND));
                openPayList.add(item);
            }

            // 微信特约商户号
            createAllPayVO.setWxSubMchid(applypayUserInfo.getSubMchid());
            result.setCreateAllPayInfo(createAllPayVO);
        }

        // 开户意愿申请单
        ApplyOpenAccountStatus queryParam = new ApplyOpenAccountStatus();
        queryParam.setAccountId(param.getAccountId());
        queryParam.setThirdUserId(thirdUserInfo.getThirdUserId());
        List<ApplyOpenAccountStatus> applyOpenAccountStatuses = applyOpenAccountStatusService.selectByParam(queryParam);
        if(applyOpenAccountStatuses.size() == 1) {
            ApplyOpenAccountVO applyOpenAccountVO = new ApplyOpenAccountVO();
            ApplyOpenAccountStatus applyOpenAccountStatus = applyOpenAccountStatuses.get(0);
            BeanUtils.copyProperties(applyOpenAccountStatus,applyOpenAccountVO);
            for(OpenAccountApplyStatusEnum status : OpenAccountApplyStatusEnum.values()) {
                if(status.getStatus() == applyOpenAccountStatus.getStatus().intValue()) {
                    applyOpenAccountVO.setStatus(status.getDesc());
                    applyOpenAccountVO.setRejectReason(applyOpenAccountStatus.getRejectReason());
                }
            }
            for(OpenAccountAuthorizeStatusEnum status : OpenAccountAuthorizeStatusEnum.values()) {
                if(status.getStatus() == applyOpenAccountStatus.getAuthorize().intValue()) {
                    applyOpenAccountVO.setAuthorize(status.getDesc());
                }
            }
            applyOpenAccountVO.setCreateTime(DateUtil.format(applyOpenAccountStatus.getCreateTime(), DateUtil.DATEFORMATSECOND));
            applyOpenAccountVO.setUpdateTime(DateUtil.format(applyOpenAccountStatus.getUpdateTime(), DateUtil.DATEFORMATSECOND));
            result.setApplyOpenAccount(applyOpenAccountVO);
        }
        return result;
    }

    private ResultVO checkOpenAccountInfoParam(OpenAccountInfoQueryParam param) {
        if(null == param || (StringUtils.isEmpty(param.getAccountId()) && (StringUtils.isEmpty(param.getBusinessId()) || StringUtils.isEmpty(param.getBusinessIdType())))
                || StringUtils.isEmpty(param.getChannelProduct())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        if(StringUtils.isEmpty(param.getAccountId())) {
            String accountId = payAccountService.getAccountIdByBusiness(param.getBusinessIdType(), param.getBusinessId());
            if(StringUtils.isEmpty(accountId)) {
                return ResultVO.createError(ResultCodeEnum.USER_NOT_FOUND);
            }
            param.setAccountId(accountId);
        }
        return ResultVO.createSuccess();
    }

    @Override
    public ResultVO updateUserInfo(UserInfoUpdateVO param) {
        log.info("OpenAccountServiceImpl updateUserInfo begin UserInfoUpdateVO:{}", JSONUtils.toJSON(param));
        ResultVO resultVO = new ResultVO();
        if (param==null|| StringUtil.isEmpty(param.getAccountId())){
            log.warn("OpenAccountServiceImpl updateUserInfo param error UserInfoUpdateVO:{}", JSONUtils.toJSON(param));
            return ResultVO.createParamError();
        }
        String accountId = param.getAccountId();
        UserInfo oldUserInfo = userInfoService.queryUserInfoByAccountId(accountId);
        if(null == oldUserInfo) {
            log.error("OpenAccountServiceImpl updateUserInfo user info is not exist. UserInfoUpdateVO:{}", JSONUtils.toJSON(param));
            return ResultVO.create(ResultCodeEnum.USER_NOT_FOUND);
        }
        // ============ 记录 避免误更 ===============
        log.info("OpenAccountServiceImpl updateUserInfo old user cipher info : {}", JSONUtils.toJSON(oldUserInfo));
        String telNo = null;
        List<com.xyy.saas.payment.pojo.dto.ContactInfo> contactInfos = null;
        UserDto basicInfo = null;
        SettleAccountDto settleInfo = null;
        Map<String, String> picInfo = null;
        try {
            String userInfoKey = ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY);
            telNo = AESUtil.decrypt(oldUserInfo.getTelNo(), userInfoKey);
            String contactInfoStr = AESUtil.decrypt(oldUserInfo.getContactInfo(), userInfoKey);
            contactInfos = JSONUtils.parse2List(contactInfoStr, com.xyy.saas.payment.pojo.dto.ContactInfo.class);
            String basicInfoStr = AESUtil.decrypt(oldUserInfo.getBasicInfo(), userInfoKey);
            basicInfo = JSONUtils.parse2Object(basicInfoStr, UserDto.class);
            String settleInfoStr = AESUtil.decrypt(oldUserInfo.getSettleInfo(), userInfoKey);
            settleInfo = JSONUtils.parse2Object(settleInfoStr, SettleAccountDto.class);
            String picInfoStr = AESUtil.decrypt(oldUserInfo.getPicInfo(), userInfoKey);
            picInfo = JSONUtils.parse2Map(picInfoStr, String.class, String.class);
        } catch (Exception e) {
            log.error("OpenAccountServiceImpl updateUserInfo AESUtil.decrypt exception. e:", e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
        log.info("OpenAccountServiceImpl updateUserInfo old user data. telNo:{}, basicInfo:{}, settleInfo:{}, contactInfos:{}", telNo, JSONUtils.toJSON(basicInfo), JSONUtils.toJSON(settleInfo), JSONUtils.toJSON(contactInfos));
        // 数据替换
        // 电话号码
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getMobile())) {
            telNo = param.getMobile();
        }
        // 这里直接替换
        if(null != param.getContactInfos() && !param.getContactInfos().isEmpty()) {
            contactInfos = param.getContactInfos();
        }
        // basicInfo
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getName())) {
            basicInfo.setName(param.getName());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getAddress())) {
            basicInfo.setAddress(param.getAddress());
        }
        // 身份信息
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getCardId())) {
            basicInfo.getIdentityInfo().setId(param.getCardId());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getIdentityType())) {
            basicInfo.getIdentityInfo().setType(param.getIdentityType());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getProfession())) {
            basicInfo.getIdentityInfo().setProfession(param.getProfession());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getValidFrom())) {
            basicInfo.getIdentityInfo().setValidFrom(param.getValidFrom());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getValidUntil())) {
            basicInfo.getIdentityInfo().setValidUntil(param.getValidUntil());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getIndustry())) {
            basicInfo.getIdentityInfo().setIndustry(param.getIndustry());
        }
        // 结算信息
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getAccountType())) {
            settleInfo.setAccountType(param.getAccountType());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getCallbackAddress())) {
            settleInfo.setCallbackAddress(param.getCallbackAddress());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getBusinessUserName())) {
            settleInfo.setBusinessUserName(param.getBusinessUserName());
        }
        //
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getAccount())) {
            settleInfo.getRecipient().setAccount(param.getAccount());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getSettleName())) {
            settleInfo.getRecipient().setName(param.getSettleName());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getSettleMobile())) {
            settleInfo.getRecipient().setMobile(param.getSettleMobile());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getOpenBank())) {
            settleInfo.getRecipient().setOpenBank(param.getOpenBank());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getSubBank())) {
            settleInfo.getRecipient().setSubBank(param.getSubBank());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getSubBankCode())) {
            settleInfo.getRecipient().setSubBankCode(param.getSubBankCode());
        }
        if(org.apache.commons.lang.StringUtils.isNotBlank(param.getCardType())) {
            settleInfo.getRecipient().setCardType(Integer.parseInt(param.getCardType()));
        }
        if(null != param.getPicMap()) {
            for (Map.Entry<String, String> item : param.getPicMap().entrySet()) {
                picInfo.put(item.getKey(), item.getValue());
            }
        }

        log.info("OpenAccountServiceImpl updateUserInfo new user data. telNo:{}, basicInfo:{}, settleInfo:{}, contactInfos:{}", telNo, JSONUtils.toJSON(basicInfo), JSONUtils.toJSON(settleInfo), JSONUtils.toJSON(contactInfos));
        try{
            //数据组装
            UserInfo userInfo = new UserInfo();
            userInfo.setContactInfo(AESUtil.encrypt(JSONUtils.toJSON(contactInfos), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            userInfo.setAccountId(accountId);
            userInfo.setTelNo(AESUtil.encrypt(telNo, ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            userInfo.setBasicInfo(AESUtil.encrypt(JSONUtils.toJSON(basicInfo), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            userInfo.setSettleInfo(AESUtil.encrypt(JSONUtils.toJSON(settleInfo), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            userInfo.setPicInfo(AESUtil.encrypt(JSONUtils.toJSON(picInfo), ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY)));
            userInfo.setUtime(new Date());
            log.info("OpenAccountServiceImpl updateUserInfo new user cipher info : {}", JSONUtils.toJSON(userInfo));
            userInfoService.updateUserInfoByUserId(userInfo);
            return resultVO;
        }catch (Exception e){
            log.error("OpenAccountServiceImpl updateUserInfo error accountId:{}, e:{}", accountId, e);
            resultVO.setCode(ResultCodeEnum.DEFAULT_ERROR.getCode());
            resultVO.setMsg(ResultCodeEnum.DEFAULT_ERROR.getMsg());
            return resultVO;
        }
    }

    @SneakyThrows
    @Override
    public UserSettleInfoVO queryUserSettleInfo(String businessIdType, String businessUserId) {
        String accountId = payAccountService.getAccountIdByBusiness(businessIdType, businessUserId);
        if (StringUtils.isBlank(accountId)) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }
        UserInfo userInfo = userInfoService.queryUserInfoByAccountId(accountId);
        if (userInfo == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }
        String userInfoKey = ApolloUtil.getProperty(ApolloConstants.USER_INFO_KEY);
        SettleAccountDto settleInfo = decryptAndParse(userInfo.getSettleInfo(), userInfoKey, SettleAccountDto.class);
        Recipient recipient = settleInfo.getRecipient();
        if (recipient == null) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }
        return new UserSettleInfoVO()
                .setAccount(recipient.getAccount())
                .setSettleName(recipient.getName())
                .setSubBank(recipient.getSubBank());
    }

    @SneakyThrows
    private <T> T decryptAndParse(String data, String key, Class<T> clazz) {
        try {
            String decryptedData = AESUtil.decrypt(data, key);
            return JSONUtils.parse2Object(decryptedData, clazz);
        } catch (Exception e) {
            log.error("Error during decryption and parsing. e:", e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

}
